#include "private_implementations/security/SecurityKeyValidator.h"
#include "PolframeworkStrings.h"
#include "interfaces/Types/PolFrameworkTagDefs.h"
#include "interfaces/Types/PolFrameworkTypeDefs.h"
#include "Interfaces/logging/ILogger.h"
#include "private_implementations/thread/SimScopedLock.h"
#include <Strsafe.h>

static const simframework::ObjectTypeID g_ObjectTypeID(SIMTEXT("polframework::SecurityKeyValidator"));
static const UINT strBuffSize = 160;

polframework::SecurityKeyValidator::SecurityKeyValidator()
{
	sharedDLLLoaderPI_.hProcess = NULL;
	sharedDLLLoaderPI_.hThread = NULL;
	TCHAR sz[strBuffSize];
	int charRead = 0;

	TCHAR currentPathBuffer[MAX_PATH];
	GetModuleFileName( NULL, currentPathBuffer, MAX_PATH );
	SimString currentFilePath(currentPathBuffer );
	SimString::size_type pos = currentFilePath.find_last_of( SIMTEXT("\\/" ));
	verifierLocation_ = currentFilePath.substr( 0, pos);

	loaderappName_ = getIntegrationResourceString(IDS_SDLL_LOADER);
	sharedDLLFileMappingObjectName_ = getIntegrationResourceString(IDS_SHARED_DLL_MEM_NAME);
	std::tr1::shared_ptr<simframework::interprocess::NonBlockingMessageQueue> sharedDLLFileMappingMemTmp(new simframework::interprocess::NonBlockingMessageQueue(sharedDLLFileMappingObjectName_));
	sharedDLLFileMappingMem_.swap(sharedDLLFileMappingMemTmp);

	//memset(sz, 0, strBuffSize);
	//if (productID_ == polframework::PRODUCT_ID_CROWDAMBIENCE)
	//{
	//	sharedMemObjName_ = getIntegrationResourceString(IDS_CAV_SHARED_MEM_NAME);
	//	verifierFileName_ = getIntegrationResourceString(IDS_CAV_FILENAME);
	//}
	//else if (productID_ == polframework::PRODUCT_ID_TRAFFIC_AMBIENCE)
	//{
	//	sharedMemObjName_ = getIntegrationResourceString(IDS_TAV_SHARED_MEM_NAME);
	//	verifierFileName_ = getIntegrationResourceString(IDS_TAV_FILENAME);
	//}
	//else if (productID_ == polframework::PRODUCT_ID_URBAN_AMBIENCE)
	//{
	//	sharedMemObjName_ = getIntegrationResourceString(IDS_UAV_SHARED_MEM_NAME);
	//	verifierFileName_ = getIntegrationResourceString(IDS_UAV_FILENAME);
	//}
	//else if (productID_ == polframework::PRODUCT_ID_INSURGENT_AMBIENCE)
	//{
	//	sharedMemObjName_ = getIntegrationResourceString(IDS_IAV_SHARED_MEM_NAME);
	//	verifierFileName_ = getIntegrationResourceString(IDS_IAV_FILENAME);
	//}
	ZeroMemory( &sharedDLLLoaderSI_, sizeof(sharedDLLLoaderSI_) );		
	ZeroMemory( &sharedDLLLoaderPI_, sizeof(sharedDLLLoaderPI_) );
	sharedDLLLoaderSI_.cb = sizeof(sharedDLLLoaderSI_);
	sharedDLLLoaderSI_.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
	sharedDLLLoaderSI_.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
	sharedDLLLoaderSI_.hStdOutput =  GetStdHandle(STD_OUTPUT_HANDLE);
	sharedDLLLoaderSI_.hStdError = GetStdHandle(STD_ERROR_HANDLE);
	sharedDLLLoaderSI_.wShowWindow = SW_HIDE;

	/*ZeroMemory(&keyProcessSI_, sizeof(keyProcessSI_));
	ZeroMemory(&keyProcessPI_, sizeof(keyProcessPI_));
	keyProcessSI_.cb = sizeof(keyProcessSI_);
	keyProcessSI_.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
	keyProcessSI_.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
	keyProcessSI_.hStdOutput =  GetStdHandle(STD_OUTPUT_HANDLE);
	keyProcessSI_.hStdError = GetStdHandle(STD_ERROR_HANDLE);
	keyProcessSI_.wShowWindow = SW_HIDE;*/
}

polframework::SecurityKeyValidator::SecurityKeyValidator( const SecurityKeyValidator& other )
{
	*this = other;
}
polframework::SecurityKeyValidator::~SecurityKeyValidator()
{	
	WaitForSingleObject(sharedDLLLoaderPI_.hProcess, 10000);
	simframework::simthread::SimScopedLock lock(lock_);
	for (LicenseKeyInfoMap::iterator itr = licenseKeyInfos_.begin(), e_itr = licenseKeyInfos_.end(); itr != e_itr; ++itr)
	{
		delete itr->second;
	}
	licenseKeyInfos_.clear();
}

polframework::SecurityKeyValidator& polframework::SecurityKeyValidator::operator=( const SecurityKeyValidator& other )
{
	if (this != &other)
	{
		//this->productID_ = other.productID_;
		for (LicenseKeyInfoMap::iterator itr = licenseKeyInfos_.begin(), e_itr = licenseKeyInfos_.end(); itr != e_itr; ++itr)
		{
			delete itr->second;
		}
		licenseKeyInfos_.clear();
		for (LicenseKeyInfoMap::const_iterator itr = other.licenseKeyInfos_.begin(), e_itr = other.licenseKeyInfos_.end(); itr != e_itr; ++itr)
		{
			licenseKeyInfos_[itr->first] = new LicenseKeyInfo(*(itr->second));
		}
		sharedDLLLoaderSI_ = other.sharedDLLLoaderSI_;
		sharedDLLLoaderPI_ = other.sharedDLLLoaderPI_;
		sharedDLLFileMappingObjectName_ = other.sharedDLLFileMappingObjectName_;
		loaderappName_ = other.loaderappName_;
		verifierLocation_ = other.verifierLocation_;
		uuidGenerator_ = other.uuidGenerator_;
		sharedDLLFileMappingMem_ = other.sharedDLLFileMappingMem_;
	}
	return *this;
}

const simframework::ObjectTypeID& polframework::SecurityKeyValidator::getObjectTypeID() const
{
	return getObjectTypeIDStatic();
}

simframework::IObject* polframework::SecurityKeyValidator::clone() const
{
	return simframework::getSimContext().getDefaultAllocator().constructCopy(this);
}

simframework::IObject* polframework::SecurityKeyValidator::clone( simframework::IAllocator& allocator ) const
{
	return allocator.constructCopy(this);
}

void polframework::SecurityKeyValidator::assign( const IObject& other )
{
	_ASSERT(getObjectTypeIDStatic() == other.getObjectTypeID());
	*this = static_cast<const SecurityKeyValidator&>(other);
}

const simframework::ObjectTypeID& polframework::SecurityKeyValidator::getObjectTypeIDStatic()
{
	return g_ObjectTypeID;
}

simframework::interprocess::IEndPoint::MessageType polframework::SecurityKeyValidator::getReplyForChallenge(ProductIDType productID, SimString challenge )
{
	LicenseKeyInfo*& lkInfo = licenseKeyInfos_[productID];
	if (lkInfo == NULL)
	{
		lkInfo = new LicenseKeyInfo;
		ZeroMemory(&(lkInfo->keyProcessSI_), sizeof(lkInfo->keyProcessSI_));
		ZeroMemory(&(lkInfo->keyProcessPI_), sizeof(lkInfo->keyProcessPI_));
		lkInfo->keyProcessSI_.cb = sizeof(lkInfo->keyProcessSI_);
		lkInfo->keyProcessSI_.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
		lkInfo->keyProcessSI_.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
		lkInfo->keyProcessSI_.hStdOutput =  GetStdHandle(STD_OUTPUT_HANDLE);
		lkInfo->keyProcessSI_.hStdError = GetStdHandle(STD_ERROR_HANDLE);
		lkInfo->keyProcessSI_.wShowWindow = SW_HIDE;

		TCHAR sz[strBuffSize];
		int charRead = 0;

		//SimString sharedMemObjName;
		SimString verifierFileName;

		memset(sz, 0, strBuffSize);
		if (productID == polframework::PRODUCT_ID_CROWDAMBIENCE)
		{
			//sharedMemObjName = getIntegrationResourceString(IDS_CAV_SHARED_MEM_NAME);
			verifierFileName = getIntegrationResourceString(IDS_CAV_FILENAME);
		}
		else if (productID == polframework::PRODUCT_ID_TRAFFIC_AMBIENCE)
		{
			//sharedMemObjName = getIntegrationResourceString(IDS_TAV_SHARED_MEM_NAME);
			verifierFileName = getIntegrationResourceString(IDS_TAV_FILENAME);
		}		
		else if (productID == polframework::PRODUCT_ID_INSURGENT_AMBIENCE)
		{
			//sharedMemObjName = getIntegrationResourceString(IDS_IAV_SHARED_MEM_NAME);
			verifierFileName = getIntegrationResourceString(IDS_IAV_FILENAME);
		}
		lkInfo->verifierFileName_ = verifierFileName;
		lkInfo->sharedMemObjName_ = uuidGenerator_.getNewUniqueUUID();//sharedMemObjName;
		std::tr1::shared_ptr<simframework::interprocess::NonBlockingMessageQueue> tempMsgQ(new simframework::interprocess::NonBlockingMessageQueue(lkInfo->sharedMemObjName_));
		lkInfo->sharedMsgQ_ = tempMsgQ;
	}

	simframework::interprocess::IEndPoint::MessageType retVal;
	SimString verifierFileLocation = verifierLocation_ + SIMTEXT("\\") + lkInfo->verifierFileName_;
	SimString loaderFileLocation = verifierLocation_ + SIMTEXT("\\") + loaderappName_;

	LPTSTR commandLine = SIMTEXT("");
	//BOOL successLoader = CreateProcess(loaderFileLocation.data(),commandLine,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&sharedDLLLoaderSI_, &sharedDLLLoaderPI_);

	DWORD dwExitCode;
	GetExitCodeProcess(sharedDLLLoaderPI_.hProcess, &dwExitCode);

	DWORD errCode = GetLastError();
	BOOL successLoader = FALSE;
	if (dwExitCode != STILL_ACTIVE)
	{			
		successLoader = CreateProcess(loaderFileLocation.data(),commandLine,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&sharedDLLLoaderSI_, &sharedDLLLoaderPI_);
		bool bSuccess = false;
		simframework::interprocess::IEndPoint::MessageType doneStr(("done"));
		simframework::interprocess::NonBlockingMessageQueue::ReceiveOperationResult popResult;
	
		if (successLoader)
		{
			while(!bSuccess)
			{
				popResult = sharedDLLFileMappingMem_->receive();
				bSuccess = popResult.first && popResult.second == doneStr;
				Sleep(100);
			}
		}	
	}
	else
	{
		successLoader = true;
	}
	/**/

	if(simframework::getSimContext().getLogger().isLoggingEnabledForType(simframework::logging::LOG_INFO))
	{
		SimStringStream message;
		message << SIMTEXT("[SecurityKeyValidator][getReplyForChallenge] [DLL Load status - ") <<loaderFileLocation.c_str() << successLoader << SIMTEXT( " ]");
		simframework::getSimContext().getLogger().log(simframework::logging::LOG_INFO, message.str());
	}

	//bool bSuccess = false;
	//simframework::interprocess::IEndPoint::MessageType doneStr(("done"));
	//simframework::interprocess::NonBlockingMessageQueue::ReceiveOperationResult popResult;

	////simframework::interprocess::NonBlockingMessageQueue sharedMem(lkInfo->sharedMemObjName_);
	////simframework::interprocess::NonBlockingMessageQueue sharedDLLFileMappingMem_(sharedDLLFileMappingObjectName_);

	//if (successLoader)
	//{
	//	while(!bSuccess)
	//	{
	//		popResult = sharedDLLFileMappingMem_->receive();
	//		bSuccess = popResult.first && popResult.second == doneStr;
	//		Sleep(100);
	//	}
	//}	

	//SimString keyProcMutexname = SIMTEXT("mutex::") + lkInfo->verifierFileName_;
	//HANDLE mutex = CreateMutex(0,FALSE,keyProcMutexname.c_str());
	/*if (mutex == NULL)
	{
		return retVal;
	}*/
	dwExitCode = -1;
	GetExitCodeProcess(lkInfo->keyProcessPI_.hProcess, &dwExitCode);

	errCode = GetLastError();
	BOOL successKP = FALSE;
	if (dwExitCode != STILL_ACTIVE)
	{	
		LPTSTR sharedMemObjName_arg = const_cast<LPTSTR>(lkInfo->sharedMemObjName_.data());
		successKP = CreateProcess(verifierFileLocation.data(), sharedMemObjName_arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &lkInfo->keyProcessSI_, &lkInfo->keyProcessPI_);
	}
	else
	{
		successKP = true;
	}
	if (WaitForSingleObject(lkInfo->keyProcessPI_.hProcess, 0) != WAIT_TIMEOUT)
	{
		successKP = FALSE;
	}

	SimString receivedPlainTextStr;
	if(simframework::getSimContext().getLogger().isLoggingEnabledForType(simframework::logging::LOG_INFO))
	{
		SimStringStream message;
		message << SIMTEXT("[SecurityKeyValidator][getReplyForChallenge] [exe Load status - ") <<verifierFileLocation.c_str() << errCode << SIMTEXT( " ]");
		simframework::getSimContext().getLogger().log(simframework::logging::LOG_INFO, message.str());
	}
	if (successKP)
	{
		if (lkInfo != NULL)
		{
			simframework::interprocess::IEndPoint::MessageType msg(reinterpret_cast<char*>(const_cast<SimString::value_type*>(challenge.data())), challenge.size()*sizeof(SimString::value_type));
			simframework::interprocess::ISender::SendOperationStatus sendopStatus = lkInfo->sharedMsgQ_->send(msg);
			simframework::interprocess::NonBlockingMessageQueue::ReceiveOperationResult resultFromClient;

			SimShort tryCount = 0;
			while(tryCount < 10)
			{
				resultFromClient = lkInfo->sharedMsgQ_->receive();
				if (resultFromClient.first == simframework::interprocess::IReceiver::ReceiveOperationStatusSuccess)
				{
					break;
				}
				Sleep(10);
				++tryCount;
			}
			retVal = resultFromClient.second;		
		}
	}
	/*if (successLoader)
	{
		simframework::interprocess::IEndPoint::MessageType stopStr(("stop"));
		sharedDLLFileMappingMem_->send(stopStr);

		WaitForSingleObject(sharedDLLLoaderPI_.hProcess,INFINITE);
		DWORD exitCode = 0;
		GetExitCodeProcess(sharedDLLLoaderPI_.hProcess,&exitCode);
		if (exitCode == 259)
		{
			//TerminateProcess(sharedDLLLoaderPI.hProcess,-1);
		}
		CloseHandle( sharedDLLLoaderPI_.hProcess );
		CloseHandle( sharedDLLLoaderPI_.hThread );
	}*/
	return retVal;
}

SimString polframework::SecurityKeyValidator::getIntegrationResourceString( SimUInt uStringID)
{
	TCHAR msgTxt[512];
	TCHAR   *pwchMem, *pwchCur;
	UINT      idRsrcBlk = uStringID / 16 + 1;
	int       strIndex  = uStringID % 16;
	HINSTANCE hModule=NULL;
	hModule = LoadLibrary(L"Polframework.dll");
	if(hModule == NULL)
	{
		return L"";
	}
	HRSRC hResource = NULL;
	hResource =FindResourceEx(hModule, RT_STRING,MAKEINTRESOURCE(idRsrcBlk),MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
	if(!hResource)
	{
		hResource = FindResourceEx(hModule, RT_STRING,MAKEINTRESOURCE(idRsrcBlk),MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
	}

	if( hResource == NULL )
	{
		return L"";
	}

	pwchMem = (TCHAR *)LoadResource( hModule, hResource );
	//pwchMem=(TCHAR *)LockResource(LoadResource( hModule, hResource ) );
	if( pwchMem == NULL )
	{
		return L"";
	}
	pwchCur = pwchMem;
	for(int i = 0; i<16; i++ )
	{
		if( *pwchCur )
		{
			int cchString = *pwchCur;  // String size in characters.
			pwchCur++;
			if( i == strIndex )
			{
				// The string has been found in the string table.

				//wcsncpy( pwchTemp, pwchCur, cchString );

#ifdef _UNICODE
				if(cchString < 512)
				{
					StringCchCopy (msgTxt,cchString+1, pwchCur);
				}else
				{
					return L"";
					//StringCchCopy (msgTxt,25, L"Error displaying message");

				}

#else
				TCHAR *pwchTemp = new TCHAR[ cchString+1 ];
				if(cchString < 512)
				{
					WideCharToMultiByte (CP_ACP,0,pwchTemp+1,*pwchTemp+1,msgTxt,512,NULL,NULL);
				}else
				{
					return L"";
				}
				delete []pwchTemp;     
#endif

				break;
				//msgTxt = pwchTemp;
			}
			pwchCur += cchString;
		}
		else
			pwchCur++;
	}
	FreeLibrary(hModule);
	return SimString(msgTxt);
}

void polframework::SecurityKeyValidator::cleanup()
{
	simframework::simthread::SimScopedLock lock(lock_);
	simframework::interprocess::IEndPoint::MessageType stopStr(("stop"));
	sharedDLLFileMappingMem_->send(stopStr);

	WaitForSingleObject(sharedDLLLoaderPI_.hProcess,INFINITE);
	DWORD exitCode = 0;
	GetExitCodeProcess(sharedDLLLoaderPI_.hProcess,&exitCode);
	if (exitCode == 259)
	{
		//TerminateProcess(sharedDLLLoaderPI.hProcess,-1);
	}	
	CloseHandle( sharedDLLLoaderPI_.hProcess );
	CloseHandle( sharedDLLLoaderPI_.hThread );
}